package model

import (
	j_log "config-client-v1/init/j-log"
	"config-client-v1/utils/tools"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/robinson/gos7"
	"github.com/simonvetter/modbus"
	"io"
	"net"
	"time"
)

// PPE 工控协议
type PPE struct {
	ID                  int                  `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"`
	ProjectID           int                  `gorm:"column:project_id;comment:工程ID" json:"project_id"` // 工程ID
	Code1               string               `gorm:"column:code1" json:"code1"`
	Code2               string               `gorm:"column:code2" json:"code2"`
	Code3               string               `gorm:"column:code3" json:"code3"`                                            // OPC,PLC专用
	ProtocolName        string               `gorm:"column:protocol_name;comment:协议名称" json:"protocol_name"`               // 协议名称
	Mode                string               `gorm:"column:mode;comment:模式" json:"mode"`                                   // [client,server]
	Name                string               `gorm:"column:name" json:"name"`                                              // 通讯名称
	Config              string               `gorm:"column:config;comment:该协议的默认配置参数, 包含IP,端口,波特率等, JSON格式" json:"config"` // 该协议的默认配置参数, 包含IP,端口,波特率等, JSON格式
	RunningState        uint8                `gorm:"column:running_state" json:"running_state"`                            // 0.异常 1.正在启动 2.已启动 3.正在停止 4.已停止 5.正在重连 6. 7.
	Detail              string               `gorm:"column:detail" json:"detail"`
	Sort                int                  `gorm:"column:sort;comment:排序" json:"sort"`                 // 排序
	CreateTime          *string              `gorm:"column:create_time;comment:添加时间" json:"create_time"` // 添加时间
	UpdateTime          *string              `gorm:"column:update_time;comment:修改时间" json:"update_time"` // 修改时间
	Del                 int                  `gorm:"column:del;comment:0.1.删除" json:"del"`               // 0.1.删除
	FrameList           []*Frame             `json:"frame_list"  gorm:"foreignKey:PPEID;references:ID"`  // foreignKey 外表外键 references 当前表关联键
	ModbusServerDB      []*ModbusServerDB    `json:"modbus_server_db"  gorm:"foreignKey:PPEID;references:ID"`
	OPCServerItem       []*OPCServerItem     `json:"opc_server_item"  gorm:"foreignKey:PPEID;references:ID"`
	FrameItemList       []*FrameItem         `json:"frame_item_list"  gorm:"-"` // 工程导出时 OPC UA|DA 客户端时专用
	RS485Client         *io.ReadWriteCloser  `json:"-" gorm:"-"`
	ModbusRTUClient     *modbus.ModbusClient `json:"-" gorm:"-"`
	ModbusTCPClient     *modbus.ModbusClient `json:"-" gorm:"-"`
	ModbusServerHandler *ModbusHandler       `gorm:"-" json:"-"` // modbus server 从站DB库
	PLCSnap7Client      *gos7.Client         `json:"-" gorm:"-"`
	OPCClientLastTime   int64                `json:"-" gorm:"-"` // opc 客户端最后心跳时间, 如果超过10秒, 则断开重连
	TcpClient           *TCPClient           `gorm:"-" json:"-"` // tcp 客户端
	TCPOnlineMap        *TCPOnlineMapStu     `gorm:"-" json:"-"` // tcp 服务端的在线列表
	TCPListener         net.Listener         `gorm:"-" json:"-"` // tcp 服务端的listener
}

type PPEExtPLC struct {
	DataConfig PLCConfig `json:"-" gorm:"-"`
	PPE
}

func (ppe *PPE) TableName() string {
	return "project_protocol_equip"
}
func (ppe *PPEExtPLC) TableName() string {
	return "project_protocol_equip"
}
func (ppe *PPE) UpdateVariablesMap() (err error) {
	//fmt.Println("--- 更新 ppe 变量到内存中")
	list := make([]*FrameItem, 0)
	err = DB.Where("ppe_id = ?", ppe.ID).Find(&list).Error
	if err != nil {
		return
	}
	for _, row := range list {
		v, ok := VariablesMap.Get(row.Addr)
		if ok {
			v.FrameItem = *row
		} else {
			variable := &Variable{
				FrameItem: *row,
			}
			VariablesMap.Set(row.Addr, variable)
		}
	}
	return
}

func (ppe *PPE) Start() (err error) {
	if len(ppe.FrameList) > 0 {
		for _, frame := range ppe.FrameList {
			err = json.Unmarshal([]byte(frame.JsonDetail), &frame.FrameItemParam)
			if err != nil {
				return errors.New(fmt.Sprintf("frame json解析失败: %s %v", ppe.Name, err.Error()))
			}
			err = frame.ValidateFrameV2(ppe)
			if err != nil {
				return
			}
		}
	}

	switch ppe.ProtocolName {
	case "CAN2.0B":
		go ppe.RunCANTCPClient()
	case "RS485":
		go ppe.RunRS485()
	case "MODBUS RTU":
		go ppe.RunModbusRTUClient()
	case "MODBUS TCP":
		config := &ModbusTCPConfig{}
		_ = json.Unmarshal([]byte(ppe.Config), config)
		if config.Mode == "server" {
			for _, db := range ppe.ModbusServerDB {
				db.PointBindList = make([]*ModbusPointBind, 0)
				err = json.Unmarshal([]byte(db.JsonDetail), &db.PointBindList)
				if err != nil {
					return errors.New(fmt.Sprintf("db json解析失败: %s %v", ppe.Name, err.Error()))
				}
				for _, row := range db.PointBindList {
					row.PPEID = ppe.ID
					row.SlaveID = db.SlaveID
					ModbusValueMap.Set(row.Addr, row)
				}
			}
			go ppe.RunModbusTCPServer()
		} else if config.Mode == "client" {
			go ppe.RunModbusTCPClient()
		} else {
			return errors.New(fmt.Sprintf("暂不支持此TCP模式, %s", config.Mode))
		}
	case "TCP/IP":
		config := &TCPIPConfig{}
		_ = json.Unmarshal([]byte(ppe.Config), config)
		if config.Mode == "server" {
			go ppe.RunTCPServer()
		} else {
			go ppe.RunTCPClient()
		}
	case "OPC Client":
		if OPCWSClient == nil {
			return errors.New("OPCWSClient 未连接, 无法启动OPC Client")
		}
		go ppe.RunOPCUAClient()
	case "OPC Server":
		if OPCWSClient == nil {
			return errors.New("OPCWSClient 未连接, 无法启动OPC Server")
		}
		go ppe.RunOPCUAServer()
	case "OPC DA Client":
		if OPCWSClient == nil {
			return errors.New("OPCWSClient 未连接, 无法启动OPC DA Client")
		}
		go ppe.RunOPCDAClient()
	case "OPC DA Server":
		if OPCWSClient == nil {
			return errors.New("OPCWSClient 未连接, 无法启动OPC DA Server")
		}
		go ppe.RunOPCDAServer()
	case "PLC":
		switch ppe.Mode {
		case "S7-1200":
			fallthrough
		case "S7-1500":
			go ppe.RunPLCSnap7()
		case "汇川":
			go ppe.RunPLCHuiChuan()
		default:
			return errors.New(fmt.Sprintf("暂不支持此PLC类型 %s", ppe.Mode))
		}
	default:
		j_log.LogError([]interface{}{ppe.Name, "暂不支持此协议", ppe.ProtocolName})
		return errors.New("暂不支持此协议" + ppe.ProtocolName)
	}

	j_log.LogInfo([]interface{}{fmt.Sprintf("【正在启动】%s:%s", ppe.ProtocolName, ppe.Name)})
	ppe.RunningState = 1
	_ = ppe.UpdateRunningState(1, "正在启动")

	startTime := time.Now()
	for {
		time.Sleep(100 * time.Millisecond)
		if ppe.RunningState == 2 {
			break
		}
		if time.Since(startTime) > time.Second*5 {
			err = errors.New(fmt.Sprintf("设备启动超时 %s: running_state=%d;%s", ppe.Name, ppe.RunningState, ppe.Detail))
			return
		}
	}
	return nil
}
func (ppe *PPE) Stop() (err error) {
	// 设置信号控制为 3
	j_log.LogInfo([]interface{}{fmt.Sprintf("【正在停止】%s:%s", ppe.ProtocolName, ppe.Name)})
	ppe.RunningState = 3
	_ = ppe.UpdateRunningState(3, "正在停止")

	switch ppe.ProtocolName {
	case "CAN2.0B":
		if ppe.TcpClient != nil && ppe.TcpClient.Connection != nil {
			_ = ppe.TcpClient.Connection.Close()
		}
	case "RS485":
	case "MODBUS RTU":
	case "PLC":
	case "MODBUS TCP":
	case "TCP/IP":
		config := &TCPIPConfig{}
		_ = json.Unmarshal([]byte(ppe.Config), config)
		if config.Mode == "server" {
			_ = ppe.TCPListener.Close()
		} else if config.Mode == "client" {
			if ppe.TcpClient != nil && ppe.TcpClient.Connection != nil {
				_ = ppe.TcpClient.Connection.Close()
			}
		} else {
			return errors.New(fmt.Sprintf("[%s] 停止失败 ,暂不支持此TCP模式 %s", ppe.Name, config.Mode))
		}
	case "OPC Client":
		if OPCWSClient == nil {
			return errors.New(fmt.Sprintf("OPCWSClient 未连接"))
		}
		req := OPCWSRequest{
			Route: "opcua/stop",
			TS:    tools.GetNow(),
			Params: map[string]interface{}{
				"ppe_id": ppe.ID,
			},
		}
		jsonB, _ := json.Marshal(&req)
		err = OPCWSClient.WriteMessage(websocket.TextMessage, jsonB)
		if err != nil {
			return errors.New(fmt.Sprintf("OPCWSClient.WriteMessage error %s", err.Error()))
		}
	case "OPC Server":
		if OPCWSClient == nil {
			return errors.New(fmt.Sprintf("OPCWSClient 未连接"))
		}
		req := OPCWSRequest{
			Route: "opcuaserver/stop",
			TS:    tools.GetNow(),
			Params: map[string]interface{}{
				"ppe_id": ppe.ID,
			},
		}
		jsonB, _ := json.Marshal(&req)
		err = OPCWSClient.WriteMessage(websocket.TextMessage, jsonB)
		if err != nil {
			return errors.New(fmt.Sprintf("OPCWSClient.WriteMessage error %s", err.Error()))
		}
	default:
		j_log.LogError([]interface{}{ppe.Name, "未知的协议", ppe.ProtocolName})
		return errors.New("未知的协议" + ppe.ProtocolName)
	}

	startTime := time.Now()
	for {
		time.Sleep(100 * time.Millisecond)
		if ppe.RunningState == 4 {
			break
		}
		if time.Since(startTime) > time.Second*10 {
			err = errors.New(fmt.Sprintf("设备停止超时 %s: running_state=%d;%s", ppe.Name, ppe.RunningState, ppe.Detail))
			return
		}
	}
	return nil
}

func (ppe *PPE) Exists(id int) (exist bool, err error) {
	err = DB.Where("id = ?", id).Limit(1).Find(ppe).Error
	if err != nil {
		return
	}
	if ppe.ID == 0 {
		return
	}
	exist = true
	return
}
func (ppe *PPE) DAServerExists() (exist bool, err error) {
	data := &PPE{}
	if ppe.ID == 0 {
		err = DB.Where("protocol_name = ?", "OPC DA Server").Limit(1).Find(data).Error
	} else {
		err = DB.Where("id != ? AND protocol_name = ?", ppe.ID, "OPC DA Server").Limit(1).Find(data).Error
	}
	if err != nil {
		return
	}
	if data.ID == 0 {
		return
	}
	exist = true
	return
}
func (ppe *PPE) Register() {
	RunningPPEMap.Set(ppe.ID, ppe)
}

func (ppe *PPE) UnRegister() {
	RunningPPEMap.Delete(ppe.ID)
	ResetRunningProject()
}

func (ppe *PPE) UpdateRunningState(state uint8, detail string) (err error) {
	WSClientMap.BroadCastEquipRunningState("ppe", ppe.ID, int(state))
	updateData := make(map[string]interface{})
	updateData["running_state"] = state
	updateData["detail"] = detail
	err = DB.Model(ppe).Where("id = ?", ppe.ID).Updates(updateData).Error
	return
}

func (ppe *PPE) ValidatePPE() (err error) {
	switch ppe.ProtocolName {
	case "CAN2.0B":
		config := &CAN20BConfig{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("CAN20BConfig json解析失败: %v", err.Error()))
		}
		if config.Mode == "" {
			return errors.New(fmt.Sprintf("请设置模式"))
		}
		if config.Mode != "server" && config.Mode != "client" {
			return errors.New(fmt.Sprintf("无效协议模式"))
		}
		if config.Mode == "server" {
			return errors.New(fmt.Sprintf("暂不支持server模式"))
		}
		if config.IP == "" {
			return errors.New(fmt.Sprintf("请设置IP"))
		}
		if !tools.IsValidIP(config.IP) {
			return errors.New(fmt.Sprintf("请设置正确的IP"))
		}
		if config.Port == 0 {
			return errors.New(fmt.Sprintf("请设置端口"))
		}
		jsonB, _ := json.Marshal(config)
		ppe.Code1 = "51"
		ppe.Mode = config.Mode
		ppe.Config = string(jsonB)
	case "RS485":
		config := &Rs485Config{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("Rs485Config json解析失败: %v", err.Error()))
		}
		jsonB, _ := json.Marshal(config)
		if config.PortName == "" {
			return errors.New(fmt.Sprintf("port_name 不可为空"))
		}
		ppe.Code1 = "52"
		ppe.Config = string(jsonB)
	case "RS232":
		config := &Rs485Config{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("Rs232Config json解析失败: %v", err.Error()))
		}
		jsonB, _ := json.Marshal(config)
		if config.PortName == "" {
			return errors.New(fmt.Sprintf("port_name 不可为空"))
		}
		ppe.Code1 = "53"
		ppe.Config = string(jsonB)
	case "MODBUS TCP":
		config := &ModbusTCPConfig{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("ModbusTCPConfig json解析失败: %v", err.Error()))
		}
		if config.Mode == "" {
			return errors.New(fmt.Sprintf("请设置模式"))
		}
		if config.Mode != "server" && config.Mode != "client" {
			return errors.New(fmt.Sprintf("无效协议模式"))
		}
		if config.IP == "" {
			return errors.New(fmt.Sprintf("请设置IP"))
		}
		if config.Port == 0 {
			return errors.New(fmt.Sprintf("请设置端口"))
		}
		if !tools.IsValidIP(config.IP) {
			return errors.New(fmt.Sprintf("请设置正确的IP"))
		}
		if config.Mode == "server" && config.MaxClients <= 0 {
			return errors.New(fmt.Sprintf("请设置最大连接客户端数量"))
		}
		if config.MaxRequestQuantity > 1000 {
			config.MaxRequestQuantity = 1000
		}
		if config.MaxRequestQuantity <= 0 {
			config.MaxRequestQuantity = 1000
		}

		jsonB, _ := json.Marshal(config)
		ppe.Code1 = "54"
		ppe.Mode = config.Mode
		ppe.Config = string(jsonB)
	case "MODBUS RTU":
		config := &ModbusRTUConfig{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("modbus_rtu json解析失败: %v %v %v", ppe.Name, config.PortName, err.Error()))
		}
		if config.PortName == "" {
			return errors.New(fmt.Sprintf("port_name 不可为空"))
		}
		jsonB, _ := json.Marshal(config)
		ppe.Code1 = "55"
		ppe.Config = string(jsonB)
	case "TCP/IP":
		config := &TCPIPConfig{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("tcp/ip json解析失败: %v %s:%d %v", ppe.Name, config.IP, config.Port, err.Error()))
		}
		if config.Mode == "" {
			return errors.New(fmt.Sprintf("请设置模式"))
		}
		if config.Mode != "server" && config.Mode != "client" {
			return errors.New(fmt.Sprintf("无效协议模式"))
		}
		if config.IP == "" {
			return errors.New(fmt.Sprintf("请设置IP"))
		}
		if config.Port == 0 {
			return errors.New(fmt.Sprintf("请设置端口"))
		}
		if !tools.IsValidIP(config.IP) {
			return errors.New(fmt.Sprintf("请设置正确的IP"))
		}
		jsonB, _ := json.Marshal(config)
		ppe.Code1 = "56"
		ppe.Mode = config.Mode
		ppe.Config = string(jsonB)
	case "OPC Client":
		config := &OPCUAClientConfig{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("OPCUAClientConfig json解析失败: %s %v", ppe.Name, err.Error()))
		}
		if config.URL == "" {
			return errors.New(fmt.Sprintf("请设置设备URL"))
		}
		jsonB, _ := json.Marshal(config)
		ppe.Code1 = "58"
		ppe.Code3 = "00"
		ppe.Config = string(jsonB)
	case "OPC Server":
		config := &OPCUAServerConfig{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("OPCUAServerConfig json解析失败: %s %v", ppe.Name, err.Error()))
		}
		jsonB, _ := json.Marshal(config)
		ppe.Code1 = "59"
		ppe.Code3 = "00"
		ppe.Config = string(jsonB)
	case "OPC DA Client":
		config := &OPCDAClientConfig{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("OPCDAClientConfig json解析失败: %s %v", ppe.Name, err.Error()))
		}
		jsonB, _ := json.Marshal(config)
		ppe.Code1 = "61"
		ppe.Code3 = "00"
		ppe.Config = string(jsonB)
	case "OPC DA Server":
		exist, err := ppe.DAServerExists()
		if exist {
			return errors.New("OPC DA Server协议只能有一个")
		}
		config := &OPCDAServerConfig{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("OPCDAServerConfig json解析失败: %s %v", ppe.Name, err.Error()))
		}
		jsonB, _ := json.Marshal(config)
		ppe.Code1 = "62"
		ppe.Code3 = "00"
		ppe.Config = string(jsonB)
	case "PLC":
		config := &PLCConfig{}
		err = json.Unmarshal([]byte(ppe.Config), config)
		if err != nil {
			return errors.New(fmt.Sprintf("PLCConfig json解析失败: %v %s:%d %v", ppe.Name, config.IP, config.Port, err.Error()))
		}
		if config.IP == "" {
			return errors.New(fmt.Sprintf("请设置IP"))
		}
		if config.Port == 0 {
			return errors.New(fmt.Sprintf("请设置端口"))
		}
		if !tools.IsValidIP(config.IP) {
			return errors.New(fmt.Sprintf("请设置正确的IP"))
		}
		plcType, ok := AllowPLCTypeModel[config.Type]
		if !ok || plcType == 0 {
			return errors.New(fmt.Sprintf("暂不支持当前型号PLC %s", config.Type))
		}
		jsonB, _ := json.Marshal(config)
		ppe.Code1 = "60"
		ppe.Mode = config.Type
		ppe.Config = string(jsonB)
	default:
		return errors.New(fmt.Sprintf("暂不支持当前协议 %s", ppe.ProtocolName))
	}
	return
}

type CANModel struct {
	ID         int     `json:"id" gorm:"column:id"`
	ProjectID  int     `gorm:"column:project_id" json:"project_id"`
	PPEID      int     `gorm:"column:ppe_id" json:"ppe_id"`
	Content    string  `gorm:"column:content" json:"content"`
	CreateTime *string `json:"create_time" gorm:"column:create_time"`
	UpdateTime *string `json:"update_time" gorm:"column:update_time"`
}

func (m *CANModel) TableName() string {
	return "can_model"
}

type CANModelExt struct {
	CANModel
	PPEInfo PPE `json:"ppe_info"  gorm:"foreignKey:PPEID;references:ID"`
}

func (m *CANModelExt) TableName() string {
	return "can_model"
}
